#ifndef __DATABASE_H
#define __DATABASE_H

#include "./sqlite/sqlite3.h"
#include <QDateTime>
#include <QByteArray>
#include <QString>
#include <QVector>

//CREATE TABLE [topics] (
//  [id] INTEGER NOT NULL CONSTRAINT [idPrimaryKey] UNIQUE, 
//  [name] TEXT NOT NULL, 
//  [category] INTEGER NOT NULL);
//
//CREATE TABLE [questions] (
//  [id] INTEGER NOT NULL, 
//  [topicid] INTEGER NOT NULL CONSTRAINT [topicidfk] REFERENCES [topics]([id]), 
//  [question] TEXT NOT NULL, 
//  [answer] TEXT NOT NULL, 
//  [value] INTEGER NOT NULL);

//CREATE TABLE [ids] (
//  [tablename] TEXT NOT NULL, 
//  [value] INTEGER NOT NULL);

class DbConnection;
class DbString
{
public:
    DbString( DbConnection & dbConnection, sqlite3_stmt *& stmt, const QString & text = QString() );
    ~DbString();
    bool hasError() const;
    const QString & errorString() const;
    DbString & operator=( const DbString & dbString );

    template< typename T1, typename T2 > DbString argNvlValue( const T1 & value, const T2 & nullValue )
    {
        if (value == nullValue)
            return arg( "NULL" );
    
        return argValue( value );
    }

    DbString & argValue( const quint32 & value );
    DbString & argValue( const int & value );
    DbString & argValue( const qint64 & value );
    DbString & argValue( const double & value );
    DbString & argValue( const QDate & value );
    DbString & argValue( const QDateTime & value );
    DbString & argValue( const QString & value );
   // DbString & argValue( const DbString & value );
    DbString & argValue( const QByteArray & value );

private:
    DbConnection & m_dbConnection;
    sqlite3_stmt *& m_stmt;
    bool m_hasError;
    QString m_errorString;
    int m_boundIndex;
};

class DbConnection
{
public:
    static DbConnection & dbConnection();
    DbConnection();
    ~DbConnection();
    bool connect();
    const QString & getLastError() const;
    sqlite3 * connectionPtr();

private:
    bool _connect();

private:
    sqlite3 * m_connPtr;
    QString m_lastError;
};

class SqlCommand
{
public:
    SqlCommand();
    SqlCommand( DbConnection & dbConnection );
    ~SqlCommand();

    sqlite3_stmt *& stmt();
    DbConnection & dbConnection();
    bool executeQuery( DbString & query );
    bool selectNewId( const QString & tableName, qint64 & newId );
    bool commit();

private:
    bool init();
    bool _exec( const QString & sqlStr, QString & errorStr );
    
private:
    DbConnection & m_dbConnection;
    bool m_commited;
    QString m_errorStr;
    bool m_isInitialized;
    sqlite3_stmt * m_stmt;
};

class Selector
{
public:
    Selector();
    Selector( DbConnection & dbConnection );
    virtual ~Selector();
    sqlite3_stmt *& stmt();
    DbConnection & dbConnection();
    bool select( DbString & commandStr, bool bShowError = true );
    bool isEmptySelect();
    bool read();

    bool isNull( int column );
    qint64 getInt64( int column );
    qint32 getInt32( int column );
    double getDouble( int column );
    QString getString( int column );
    QDate getDate( int column );
    QDateTime getDateTime( int column );
    QByteArray getBytea( int column );

    qint64 getNvlInt64( int column, qint64 defaultValue );
    qint32 getNvlInt32( int column, qint32 defaultValue );
    double getNvlDouble( int column, double defaultValue );
    QString getNvlString( int column, const QString & defaultValue );
    QDate getNvlDate( int column, const QDate & defaultValue );
    QDateTime getNvlDateTime( int column, const QDateTime & defaultValue );
    QByteArray getNvlBytea( int column, const QByteArray & defaultValue );

private:
    bool _isNull( int column );
    qint64 _getInt64( int column );
    qint32 _getInt32( int column );
    double _getDouble( int column );
    QString _getString( int column );
    QDate _getDate( int column );
    QDateTime _getDateTime( int column );
    QByteArray _getBytea( int column );

protected:
    DbConnection & m_dbConnection;
    sqlite3_stmt * m_stmt;
    int m_actualRow;
    bool m_isEmpty;
};

#endif
